//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Research.eResearch;
using System.Reflection;

namespace TridentAPI
{
    /// <summary>
    /// This class provides access to the run time support that
    /// a running WF gets when running under the trident Framework.
    /// The running WF's can call the static method of this class
    /// to obtain the contect object which contains information
    /// specific to that WF isntance.
    /// </summary>
    [Serializable]
    public class Context
    {
        /// <summary>
        /// Static method for creating the contect object.
        /// </summary>
        /// <param name="registryConnection"></param>
        /// <param name="activityInstance"></param>
        /// <param name="jobId"></param>
        /// <returns></returns>
        public static void Create(Connection registryConnection, ActivityInstance instance, Guid jobId)
        {
            ctx = new Context();
            ctx.registryHandle = registryConnection.Clone();
            ctx.registryHandle.Open();
            ctx.wfInstance = instance;
            ctx.wfJobId = jobId;
        }

        /// <summary>
        /// Static method for creating the contect object.
        /// </summary>
        /// <param name="connectionDetails"></param>
        /// <param name="jobId"></param>
        /// <returns></returns>
        public static Context Get()
        {
            if (ctx == null)
                Load();
            if (ctx == null)
                throw new InvalidOperationException("Host needs to call Create() method");
            return ctx;
        }

        public static byte[] GetContext()
        {
            if (ctx == null)
                return null;

            System.IO.MemoryStream mem = new System.IO.MemoryStream();
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter fm = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            fm.Serialize(mem, ctx.registryHandle);           
            return mem.ToArray();
        }

        private static void Load()
        {
            Assembly[] asmList = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly a in asmList)
            {
                if (a.FullName == typeof(Context).Assembly.FullName)
                {
                    // Check if this is initialized
                    System.Type ctxType = a.GetType(typeof(Context).FullName);
                    PropertyInfo ctxTest = ctxType.GetProperty("IsInitialized", BindingFlags.Public | BindingFlags.Static);
                    bool ctxTestOk = (bool)ctxTest.GetValue(null, new object[] { });

                    if (ctxTestOk)
                    {
                        //System.Diagnostics.Debugger.Break();
                        MethodInfo ctxGet = ctxType.GetMethod("GetContext", BindingFlags.Public | BindingFlags.Static);
                        object ctxObj = ctxGet.Invoke(null, new object[] { });

                        byte[] data = (byte[])ctxObj;
                        System.IO.MemoryStream mem = new System.IO.MemoryStream(data);

                        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter fm = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                        Connection conn = (Connection)fm.Deserialize(mem);

                        break;
                    }
                }
            }
        }

        /// <summary>
        /// Recursively goes to the children and get the activity sequence whose name is same as the unique name.
        /// </summary>
        /// <param name="rootSequence">The root sequence.</param>
        /// <param name="uniqueName">Name of the unique.</param>
        /// <returns></returns>
        private static ActivitySequence GetActivitySequence(ActivitySequence rootSequence, string uniqueName)
        {
            ActivitySequence sequence = null;
            if (rootSequence.Name.Equals(uniqueName, StringComparison.Ordinal))
            {
                sequence = rootSequence;
            }
            else
            {
                foreach (ActivitySequence childSequence in rootSequence.Children)
                {
                    sequence = GetActivitySequence(childSequence, uniqueName);
                    if (sequence != null)
                    {
                        break;
                    }
                }
            }
            return sequence;
        }

        #region Public Properties

        /// <summary>
        /// Instance of the registry connection.
        /// </summary>
        public Connection Connection
        {
            get { return this.registryHandle; }
        }

        /// Object which represents the running WF instance to
        /// which the data products are attached to.
        /// </summary>
        public ActivityInstance WFInstance
        {
            get { return this.wfInstance; }
        }

        /// <summary>
        /// JobId which represents the running job instance.
        /// </summary>
        public Guid WFJobId
        {
            get { return this.wfJobId; }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Gets the activity sequence based on the unique name.
        /// </summary>
        /// <param name="uniqueName">Name of the unique.</param>
        /// <returns></returns>
        public ActivitySequence GetActivitySequence(string uniqueName)
        {
            ActivitySequence activitySequence = null;
            if (this.wfInstance != null)
            {
                activitySequence = Context.GetActivitySequence(this.wfInstance.Activity.BaseSequence, uniqueName);
            }

            return activitySequence;
        }

        #endregion

        private Connection registryHandle = null;

        private ActivityInstance wfInstance;

        private Guid wfJobId;

        private static Context ctx;


    }
}
